package idgenerator

import (
	"fmt"
	"math"
	"miomp-terminal-suite/backend/app/common"
	"miomp-terminal-suite/backend/log"
	"miomp-terminal-suite/backend/pkg/idgenerator/model"
	"strconv"
	"time"
)

const (
	MODULE_DEFAULT = "default"
	MODULE_LOG     = "log"
	MODULE_SYSTEM  = "system"
)

var moduleMap map[string]*idModule

var updateModuleMapChan chan *idModule

type idModule struct {
	module     string
	step       int64
	bufferSize int64
	counter    int64
	idChan     chan int64
}

func Init() {

	idModuleCounterArray, err := model.ListAllIdModuleCounter()
	if err != nil {
		// 不存在
		idModuleCounter, err := model.AddIdModuleCounter(MODULE_DEFAULT)
		if err != nil {
			log.LOG.Error("Id生成器启动失败")
		}
		idModuleCounterArray = append(idModuleCounterArray, idModuleCounter)
	}

	updateModuleMapChan = make(chan *idModule, len(idModuleCounterArray))
	moduleMap = make(map[string]*idModule, len(idModuleCounterArray))

	for _, idModuleCounter := range idModuleCounterArray {
		// 构造 moduleMap
		idModule := new(idModule)
		idModule.module = idModuleCounter.Module
		idModule.step = idModuleCounter.Step
		idModule.bufferSize = int64(math.Ceil(float64(idModule.step) * 1.2))
		idModule.counter = idModuleCounter.Counter
		idModule.idChan = make(chan int64, idModule.bufferSize)

		moduleMap[idModule.module] = idModule

		// 用channel更新
		go updateIdModule()
		updateModuleMapChan <- idModule
	}
}

// updateIdModule 更新Map, 刷新idChan
func updateIdModule() {
	// TODO 分布式锁开始

	idModule := <-updateModuleMapChan

	idModuleCounter, err := model.GetIdModuleCounter(idModule.module)
	if err != nil {
		return
	}

	// 更新本地counter
	idModule.bufferSize = int64(math.Ceil(float64(idModule.step) * 1.2))
	idModule.counter = idModuleCounter.Counter

	// 更新数据库counter
	idModuleCounter.Counter = idModuleCounter.Counter + idModuleCounter.Step
	err = model.UpdateIdModuleCounter(idModuleCounter)
	if err != nil {
		return
	}
	// 访问数据库结束

	// 生成 连续Id
	if int64(cap(idModule.idChan)) != idModule.bufferSize {
		close(idModule.idChan)
		idModule.idChan = make(chan int64, idModule.bufferSize)
	}

	for i := int64(0); i < idModule.step; i++ {
		idModule.idChan <- idModule.counter + i
	}

	// 用channel更新
	updateModuleMap(idModule)
	// TODO 分布式锁结束
}

// updateModuleMap 更新本地的ModuleMap
func updateModuleMap(updateIdModule *idModule) {
	// TODO 写锁
	moduleMap[updateIdModule.module] = updateIdModule
}

// checkIdChan 检查是否需要更新 idChan
func checkIdChan(sysIdModule *idModule) {
	if int64(len(sysIdModule.idChan)) == (sysIdModule.bufferSize - sysIdModule.step) {
		// 用channel更新
		updateModuleMapChan <- sysIdModule
		go updateIdModule()
	}
}

func GetId() (int64, error) {
	return GetIdByModule(MODULE_DEFAULT)
}

func GetIdByModule(module string) (id int64, err error) {

	var sysIdModule *idModule
	var hasModule bool

	// TODO  获取写锁
	sysIdModule, hasModule = moduleMap[module]
	if !hasModule {
		return 0, new(common.ResError).WithBiz(common.BizDefaultError)
	}
	go checkIdChan(sysIdModule)

	id = <-sysIdModule.idChan

	if id == 0 {
		id, err = GetIdByModule(module)
		if err != nil {
			id = time.Now().UnixMilli() * 100
		}
	}

	log.LOG.Info(fmt.Sprintf("生成的Id===> %s", strconv.FormatInt(id, 10)))

	return id, nil
}
